package com.pocket.insight.base.mql;

import com.pocket.insight.base.exception.BizException;
import com.pocket.insight.base.sql.Dialect;
import com.pocket.insight.base.sql.dialect.InstDialect;
import com.pocket.insight.base.utils.ParamAttrUtil;
import com.pocket.insight.entity.ME;
import com.pocket.insight.entity.Type;
import dm.jdbc.util.StringUtil;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 描述：
 *
 * @author shuize
 * @create 2024/12/5
 */
public class BaseEval implements Eval{

    protected Dialect  dialect = new InstDialect();
    protected MQL      mql     = new MQL();
    public    List<ME> filter;

    public static TypedVal val(Object str) {
        return new TypedVal(str);
    }

    @Override
    public TypedVal evaluate(ME expr) {
        return this.eval(expr);
    }

    @Override
    public ME traverse(ME expr) {
        if (expr.kind == ME.Kind.formula) {
            return this.traverse(parseFormula(expr));
        } else {
            ME.handleLegacyAttributes(expr);
            ME e = new ME();
            e.kind = expr.kind;
            e.uid = expr.uid;
            e.dataset = expr.dataset;
            if (e.dataset instanceof Number && !(e.dataset instanceof Long)) {
                e.dataset = ((Number)e.dataset).longValue();
            }
            if (expr.op != null) {
                if (e.kind != ME.Kind.constant && e.kind != ME.Kind.field && e.kind != ME.Kind.dataset && e.kind != ME.Kind.reference && e.kind != ME.Kind.param && e.kind != ME.Kind.attr && e.kind != ME.Kind.function && e.kind != ME.Kind.measure) {
                    if (e.kind != ME.Kind.array && e.kind != ME.Kind.set) {
                        if (e.kind == ME.Kind.casewhen) {
                            e.op = this.traverseList((List)expr.op);
                        } else {
                            e.op = this.traverse(ME.from(expr.op));
                        }
                    } else {
                        e.op = ((List)expr.op).stream().map((op) -> {
                            if (op == null) {
                                return null;
                            } else {
                                return op instanceof String ? ME.create(ME.Kind.constant, op) : this.traverse(ME.from(op));
                            }
                        }).collect(Collectors.toList());
                    }
                } else {
                    e.op = expr.op;
                }
            }

            e.type = expr.type;
            e.layer = expr.layer;
            e.value = expr.value;
            e.remaining = expr.remaining;
            e.including = expr.including;
            e.direction = expr.direction;
            e.args = Collections.singletonList(this.traverseList(expr.args));
            e.window = this.traverseWindow(expr.window);
            e.within = this.traverseList(Collections.singletonList(expr.within));
            e.filter = this.traverseList(Collections.singletonList(expr.filter));
            e.orderByOut = expr.orderByOut;
            e.datasetForceNative = expr.datasetForceNative;
            e.baseUid = expr.baseUid;
            e.nullsOrder = expr.nullsOrder;
            return e;
        }
    }

    @Override
    public void traverseLight(ME expr) {

    }

    @Override
    public ME.Window traverseWindow(ME.Window paramWindow) {
        return null;
    }


    public TypedVal eval(ME expr) {
        if (expr == null) {
            return null;
        }
        switch (expr.kind) {
            case field:
                return evalField(expr);
            case function:
                return evalFunction(expr);
            case reference:
                return evalReference(StringUtil.toString(expr.op));
            case constant:
                return typedObjectToSqlLiteral(expr.type, expr.op);
            case formula:
                return eval(parseFormula(expr));
            case dataset:
                return evalDataset(expr);
            case array:
                return evalArray((List<Object>)expr.op);
            case set:
                return evalSet((List<Object>)expr.op);
            case casewhen:
                return evalCaseWhen(expr);
            case param:
                return evalParam(expr);
            case attr:
                return evalAttr(expr);
        }
        return null;
    }

    protected TypedVal evalField(ME expr) {
        return null;
    }

    protected TypedVal evalReference(String sid) {
        return null;
    }

    protected TypedVal typedObjectToSqlLiteral(Type.TypeName type, Object value) {
        return null;
    }

    protected TypedVal evalFunction(ME expr) {
        return null;
    }


    protected TypedVal evalDataset(ME expr) {
        return val(this.dialect.quoteTableName(expr.op.toString()));
    }

    protected TypedVal evalArray(List<Object> objects) {
        return null;
    }

    protected TypedVal evalSet(List<Object> objects) {
        return null;
    }

    protected TypedVal evalCaseWhen(ME expr) {
        return null;
    }

    protected TypedVal evalParam(ME expr) {
        return null;
    }

    protected TypedVal evalAttr(ME expr) {
        return null;
    }

    protected List<ME> traverseList(List<Object> exprs) {
        List<Object> list = exprs;
        return exprs == null ? null : list.stream().filter(Objects::nonNull).map(expr -> this.traverse(ME.from(expr))).collect(Collectors.toList());
    }

    public ME parseFormula(final ME expr) {
        if (ParamAttrUtil.isStringContainsAttrOrParamInReplacementFormat(StringUtil.toString(expr.op))) {
            throw new BizException("ERROR_FORMULA_CONTAINS_WRONG_FORMAT_PARAM_OR_ATTR");
        } else {
            ME he = this.mql.parse(StringUtil.toString(expr.op));
            if (he == null) {
                throw new BizException("");
            } else {
                he.setType(expr.type);
                if (expr.uid != null) {
                    he.setUid(expr.uid);
                }
                he.layer = expr.layer;
                he.addFilter(expr.filter);
                if (he.direction == null) {
                    he.direction = expr.direction;
                }
                if (expr.baseUid != null) {
                    he.setBaseUid(expr.baseUid);
                }
                Long datasetId;
                if (expr.dataset != null) {
                    if (!(he.dataset instanceof Long)) {
                        datasetId = ME.parseDatasetId(expr.dataset);
                        if (datasetId != null) {
                            expr.dataset = datasetId;
                        }
                    }
                    ME result = (new BaseEval() {
                        public ME traverse(ME e) {
                            e = super.traverse(e);
                            if (e.kind == ME.Kind.field && e.dataset == null) {
                                e.dataset = expr.dataset;
                            }
                            return e;
                        }
                    }).traverse(he);
                    result.dataset = expr.dataset;
                    return result;
                } else {
                    if (he.kind == ME.Kind.field && he.dataset != null && !(he.dataset instanceof Long)) {
                        datasetId = ME.parseDatasetId(he.dataset);
                        if (datasetId != null) {
                            he.dataset = datasetId;
                        }
                    }
                    return he;
                }
            }
        }
    }
}
